package com.ruoyi.web.controller.ma;



import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.alibaba.fastjson2.TypeReference;
import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;


import com.ruoyi.common.wxUtils.WechatPayConfig;
import com.ruoyi.common.wxUtils.WechatPayRequest;
import com.ruoyi.common.wxUtils.WechatPayUrlEnum;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;

import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.util.EntityUtils;
import org.springframework.web.bind.annotation.*;

import javax.annotation.Resource;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.*;


/**
 * @Author: Zzz
 * @Description:
 **/
@Api(tags = "支付接口(API3)")
@RestController
@RequestMapping("/test")

public class PayController {

    @Resource
    private WechatPayConfig wechatPayConfig;

    @Resource
    private WechatPayRequest wechatPayRequest;

    /**
     * 无需应答签名
     */
    @Resource
    private CloseableHttpClient wxPayNoSignClient;


    @ApiOperation(value = "根据订单号查询订单-统一接口", notes = "根据订单号查询订单-统一接口")
    @ApiOperationSupport(order = 15)
    @GetMapping("/transactions/{orderNo}")
    public  Map<String, Object> transactionsByOrderNo(@PathVariable("orderNo") String orderNo) {

        // TODO 如果是扫码支付时，该接口就很有必要，应该前端通过轮询的方式请求该接口查询订单是否支付成功

        System.out.println("根据订单号查询订单，订单号： {}"+ orderNo);

        String url = wechatPayConfig.getBaseUrl().concat(WechatPayUrlEnum.ORDER_QUERY_BY_NO.getType().concat(orderNo))
                .concat("?mchid=").concat(wechatPayConfig.getMchId());

        String res = wechatPayRequest.wechatHttpGet(url);
        System.out.println("查询订单结果：{}"+res);

        Map<String, Object> resMap = JSONObject.parseObject(res, new TypeReference<Map<String, Object>>(){});

        String outTradeNo = resMap.get("out_trade_no").toString();
        String appId = resMap.get("appid").toString();
        String mchId = resMap.get("mchid").toString();
        /**
         *         交易状态，枚举值：
         *         SUCCESS：支付成功
         *         REFUND：转入退款
         *         NOTPAY：未支付
         *         CLOSED：已关闭
         *         REVOKED：已撤销（仅付款码支付会返回）
         *         USERPAYING：用户支付中（仅付款码支付会返回）
         *         PAYERROR：支付失败（仅付款码支付会返回）
         */
        String tradeState = resMap.get("trade_state").toString();

        System.out.println("outTradeNo："+outTradeNo);
        System.out.println("appId："+appId);
        System.out.println("mchId："+mchId);
        System.out.println("tradeState："+tradeState);

        return resMap;
    }

    /**
     * 关闭(取消)订单
     * @param orderNo
     * @return
     */
    @ApiOperation(value = "关闭(取消)订单-统一接口", notes = "关闭(取消)订单-统一接口")
    @ApiOperationSupport(order = 20)
    @PostMapping("/closeOrder/{orderNo}")
    public void closeOrder(@PathVariable("orderNo") String orderNo) {

        // TODO 用于在客户下单后，不进行支付，取消订单的场景
        System.out.println("根据订单号取消订单，订单号： {}"+ orderNo);

        String url = String.format(WechatPayUrlEnum.CLOSE_ORDER_BY_NO.getType(), orderNo);
        url = wechatPayConfig.getBaseUrl().concat(url);

        // 设置参数
        Map<String, String> params = new HashMap<>(2);
        params.put("mchid", wechatPayConfig.getMchId());

        String paramsStr = JSON.toJSONString(params);
        System.out.println("请求参数 ===> {}" + paramsStr);

        String res = wechatPayRequest.wechatHttpPost(url,paramsStr);
    }

    /**
     * 申请退款
     * @param orderNo
     */
    @ApiOperation(value = "申请退款-统一接口", notes = "申请退款-统一接口")
    @ApiOperationSupport(order = 25)
    @PostMapping("/refundOrder/{orderNo}")
    public void refundOrder(@PathVariable("orderNo") String orderNo) {

        System.out.println("根据订单号申请退款，订单号： {}"+ orderNo);

        String url = wechatPayConfig.getBaseUrl().concat(WechatPayUrlEnum.DOMESTIC_REFUNDS.getType());

        // 设置参数
        Map<String, Object> params = new HashMap<>(2);
        // 订单编号
        params.put("out_trade_no", orderNo);
        // 退款单编号 - 自定义
        int outRefundNo = new Random().nextInt(999999999);
        System.out.println("退款申请号：{}"+outRefundNo);
        params.put("out_refund_no",outRefundNo+"");
        // 退款原因
        params.put("reason","申请退款");
        // 退款通知回调地址
        params.put("notify_url", wechatPayConfig.getRefundNotifyUrl());

        Map<String, Object>  amountMap =new HashMap<>();
        //退款金额，单位：分
        amountMap.put("refund", 1);
        //原订单金额，单位：分
        amountMap.put("total", 1);
        //退款币种
        amountMap.put("currency", "CNY");
        params.put("amount", amountMap);

        String paramsStr = JSON.toJSONString(params);
        System.out.println("请求参数 ===> {}" + paramsStr);


        String res = wechatPayRequest.wechatHttpPost(url,paramsStr);

        System.out.println("退款结果：{}"+res);
    }

    /**
     * 查询单笔退款信息
     * @param refundNo
     * @return
     */
    @ApiOperation(value = "查询单笔退款信息-统一接口", notes = "查询单笔退款信息-统一接口")
    @ApiOperationSupport(order = 30)
    @GetMapping("/queryRefundOrder/{refundNo}")
    public  Map<String, Object> queryRefundOrder(@PathVariable("refundNo") String refundNo) {

        System.out.println("根据订单号查询退款订单，订单号： {}"+ refundNo);

        String url = wechatPayConfig.getBaseUrl().concat(WechatPayUrlEnum.DOMESTIC_REFUNDS_QUERY.getType().concat(refundNo));

        String res = wechatPayRequest.wechatHttpGet(url);
        System.out.println("查询退款订单结果：{}"+res);

        Map<String, Object> resMap = JSONObject.parseObject(res, new TypeReference<Map<String, Object>>(){});

        String successTime = resMap.get("success_time").toString();
        String refundId = resMap.get("refund_id").toString();
        /**
         * 款到银行发现用户的卡作废或者冻结了，导致原路退款银行卡失败，可前往商户平台-交易中心，手动处理此笔退款。
         * 枚举值：
         * SUCCESS：退款成功
         * CLOSED：退款关闭
         * PROCESSING：退款处理中
         * ABNORMAL：退款异常
         */
        String status = resMap.get("status").toString();

        /**
         * 枚举值：
         * ORIGINAL：原路退款
         * BALANCE：退回到余额
         * OTHER_BALANCE：原账户异常退到其他余额账户
         * OTHER_BANKCARD：原银行卡异常退到其他银行卡
         */
        String channel = resMap.get("channel").toString();

        System.out.println("successTime："+successTime);
        System.out.println("channel："+channel);
        System.out.println("refundId："+refundId);
        System.out.println("status："+status);

        // TODO 在查询单笔退款信息时，可以再去查询一次订单的状态，保证该订单已经退款完毕了

        return resMap;
    }

    /**
     * 申请交易账单
     * @param billDate 格式yyyy-MM-dd 仅支持三个月内的账单下载申请 ，如果传入日期未为当天则会出错
     * @param billType 分为：ALL、SUCCESS、REFUND
     * ALL：返回当日所有订单信息（不含充值退款订单）
     * SUCCESS：返回当日成功支付的订单（不含充值退款订单）
     * REFUND：返回当日退款订单（不含充值退款订单）
     * @return
     */
    @ApiOperation(value = "申请交易账单-统一接口", notes = "申请交易账单-统一接口")
    @ApiOperationSupport(order = 35)
    @GetMapping("/tradeBill")
    public  String tradeBill(@RequestParam("billDate") String billDate, @RequestParam("billType")  String billType) {

        System.out.println("申请交易账单，billDate："+billDate+"，billType：{}"+ billType);

        String url = wechatPayConfig.getBaseUrl().concat(WechatPayUrlEnum.TRADE_BILLS.getType())
                .concat("?bill_date=").concat(billDate).concat("&bill_type=").concat(billType);

        String res = wechatPayRequest.wechatHttpGet(url);
        System.out.println("查询退款订单结果：{}"+res);

        Map<String, Object> resMap = JSONObject.parseObject(res, new TypeReference<Map<String, Object>>(){});

        String downloadUrl = resMap.get("download_url").toString();

        return downloadUrl;
    }

    /**
     *
     * @param billDate 格式yyyy-MM-dd 仅支持三个月内的账单下载申请，如果传入日期未为当天则会出错
     * @param accountType 分为：BASIC、OPERATION、FEES
     * BASIC：基本账户
     * OPERATION：运营账户
     * FEES：手续费账户
     * @return
     */
    @ApiOperation(value = "申请资金账单-统一接口", notes = "申请资金账单-统一接口")
    @ApiOperationSupport(order = 40)
    @GetMapping("/fundFlowBill")
    public String fundFlowBill(@RequestParam("billDate") String billDate, @RequestParam("accountType")  String accountType) {

        System.out.println("申请交易账单，billDate："+billDate+"，accountType：{}"+accountType);

        String url = wechatPayConfig.getBaseUrl().concat(WechatPayUrlEnum.FUND_FLOW_BILLS.getType())
                .concat("?bill_date=").concat(billDate).concat("&account_type=").concat(accountType);

        String res = wechatPayRequest.wechatHttpGet(url);
        System.out.println("查询退款订单结果：{}"+res);

        Map<String, Object> resMap = JSONObject.parseObject(res, new TypeReference<Map<String, Object>>(){});

        String downloadUrl = resMap.get("download_url").toString();

        return downloadUrl;
    }


    @ApiOperation(value = "下载账单-统一接口", notes = "下载账单-统一接口")
    @ApiOperationSupport(order = 45)
    @GetMapping("/downloadBill")
    public void downloadBill(String downloadUrl) {

        System.out.println("下载账单，下载地址：{}"+downloadUrl);

        HttpGet httpGet = new HttpGet(downloadUrl);
        httpGet.addHeader("Accept", "application/json");

        CloseableHttpResponse response =null;
        try {
            //使用wxPayClient发送请求得到响应
            response =  wxPayNoSignClient.execute(httpGet);

            String body = EntityUtils.toString(response.getEntity());

            int statusCode = response.getStatusLine().getStatusCode();
            if (statusCode == 200 || statusCode == 204) {
                System.out.println("下载账单，返回结果 = " + body);
            } else {
                throw new RuntimeException("下载账单异常, 响应码 = " + statusCode+ ", 下载账单返回结果 = " + body);
            }
            // TODO 将body内容转为excel存入本地或者输出到浏览器，演示存入本地
            writeStringToFile(body);

        }catch (Exception e){
            throw new RuntimeException(e.getMessage());
        }
        finally {
            if(response!=null) {
                try {
                    response.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void writeStringToFile(String body) {
        FileWriter fw = null;
        try {
            String filePath = "C:\\Users\\lhz12\\Desktop\\wxPay.txt";
            fw = new FileWriter(filePath, true);
            BufferedWriter  bw = new BufferedWriter(fw);
            bw.write(body);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(fw!=null) {
                    fw.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
